%% module stuff
-module(gossip).
-compile(export_all).

%% to run:
%% Procs = start_program().
%% printAll(Procs).
%% randomTell(Procs,3). %%Start gossiping!
%% printAll(Procs).
%% randomTell(Procs,8). %%Other gossip
%% printAll(Procs).
 
%% Start program
start_program() ->
   {S1, S2, S3} = now(),
   io:fwrite("Initial seed: ~B ~B ~B\n", [S1, S2, S3]),
   random:seed({S1, S2, S3}),
   All = spawn_procs(1000),
   seedAll(All),
   tellAll(All),
   All.

%% Tell all processes to print their current value
printAll([]) -> ok;
printAll([X|Xs]) ->
   X ! print,
   printAll(Xs).

%% Tell one random process to start gossiping to
%% one other random process. This is a chain reaction.
randomTell(Ls,Message) ->
   io:fwrite("randomTell"),
   get_random_proc(Ls) ! {message, Message}.

%% Tell all processes about all processes.
tellAll(All) ->
   [P ! {tell_all_others,All}
   || P <- All].

%% Seed all processes' PRNGs with random values.
seedAll([]) -> ok;
seedAll([P|Ps]) ->
   S1 = random:uniform(1000),
   S2 = random:uniform(1000),
   S3 = random:uniform(1000),
   P ! {seed, {S1, S2, S3}},
   seedAll(Ps).

%% Creating process pool
spawn_procs(Len) ->
   [spawn(gossip,
          proc_gossip,
          [1,[]]
         )
   || _<-lists:seq(1,Len)].
   
%% process for receiving a gossip
%% and for receiving a listprocess 
%% for receiving a gossip
proc_gossip(State,Others) ->
   receive
      {tell_all_others,NewOthers} ->
         io:fwrite("now_I_know_about_others, I'm ~p\n", [self()]),
         proc_gossip(State,NewOthers);
      {message,NewState} ->
         %% Only update if the new state is higher than the current state
         case NewState > State of
            false ->
               %io:fwrite("nope, I'm ~p, old state ~B\n", [self(), State]),
%              get_random_proc_not(Others, self()) ! {message,NewState},
               proc_gossip(NewState,Others);
            true  ->
               %io:fwrite("now_I_know_the_message, I'm ~p, old state ~B new state ~B\n", [self(), State,NewState]),
               io:fwrite("value!\n"),
               randomTell(Others,NewState), % send on to others
               %get_random_proc_not(Others, self()) ! {message,NewState},
               io:fwrite("i did randomtell\n"),
               proc_gossip(NewState,Others)
         end;
      print ->
         io:fwrite("Message: ~B, process ~p\n", [State, self()]),
         proc_gossip(State,Others);
      {seed, {V1, V2, V3}} ->
         io:fwrite("Proc ~p Seed ~B ~B ~B\n", [self(), V1, V2, V3]),
         random:seed({V1, V2, V3}),
         proc_gossip(State, Others);
      die ->
         ok
   end.

%% Get a random process that is not myself
get_random_proc_not(Procs, Me) ->
   Try = get_random_proc(Procs),
   case Try == Me of
      false ->
         Try;
      true ->
         get_random_proc_not(Procs, Me)
   end.

%% Get a random process out of a given list
get_random_proc(Procs) ->
   NumberToGet = random:uniform(length(Procs)),
   lists:nth(NumberToGet,Procs).


